﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using System.Linq.Expressions;
using BReusable;
using System.Data.Linq.Mapping;


public static class LinqExtensions
{

	/// <summary>
	/// Combines 2 linq expressions
	/// From BReusable
	/// </summary>
	/// <typeparam name="TEntity"></typeparam>
	/// <param name="expr1"></param>
	/// <param name="expr2"></param>
	/// <returns></returns>
	/// <remarks>From Marc Gravell via: http://stackoverflow.com/questions/457316/combining-two-expressions-expressionfunct-bool</remarks>
	public static Expression<Func<TEntity, bool>> And<TEntity>(
		this Expression<Func<TEntity, bool>> expr1,
		Expression<Func<TEntity, bool>> expr2)
	{
		ParameterExpression param = expr1.Parameters[0];
		if (ReferenceEquals(param, expr2.Parameters[0]))
		{
			return Expression.Lambda<Func<TEntity, bool>>(Expression.And(expr1.Body, expr2.Body), param);
		}
		return Expression.Lambda<Func<TEntity, bool>>(
			Expression.And(expr1.Body, Expression.Invoke(expr2, param)), param);
	}


	/// <summary>
	/// Combines 2 linq expressions
	/// From BReusable
	/// </summary>
	/// <typeparam name="TEntity"></typeparam>
	/// <param name="expr1"></param>
	/// <param name="expr2"></param>
	/// <remarks>From Marc Gravell via: http://stackoverflow.com/questions/457316/combining-two-expressions-expressionfunct-bool</remarks>
	/// <returns></returns>
	public static Expression<Func<TEntity, bool>> AndAlso<TEntity>(
		this Expression<Func<TEntity, bool>> expr1,
		Expression<Func<TEntity, bool>> expr2)
	{
		ParameterExpression param = expr1.Parameters[0];
		if (ReferenceEquals(param, expr2.Parameters[0]))
		{
			return Expression.Lambda<Func<TEntity, bool>>(Expression.AndAlso(expr1.Body, expr2.Body), param);
		}
		return Expression.Lambda<Func<TEntity, bool>>(

			Expression.AndAlso(expr1.Body, Expression.Invoke(expr2, param)), param);
	}

	/// <summary>
	/// Combines 2 linq expressions
	/// From BReusable
	/// </summary>
	/// <typeparam name="TEntity"></typeparam>
	/// <param name="expr1"></param>
	/// <param name="expr2"></param>
	/// <remarks>inspired by Marc Gravell via: http://stackoverflow.com/questions/457316/combining-two-expressions-expressionfunct-bool</remarks>
	/// <returns></returns>
	public static Expression<Func<TEntity, bool>> Or<TEntity>(
		this Expression<Func<TEntity, bool>> expr1,
		Expression<Func<TEntity, bool>> expr2)
	{
		ParameterExpression param = expr1.Parameters[0];
		if (ReferenceEquals(param, expr2.Parameters[0]))
		{
			return Expression.Lambda<Func<TEntity, bool>>(Expression.Or(expr1.Body, expr2.Body), param);
		}
		return Expression.Lambda<Func<TEntity, bool>>(

			Expression.Or(expr1.Body, Expression.Invoke(expr2, param)), param);
	}

	/// <summary>
	/// Combines 2 linq expressions
	/// From BReusable
	/// </summary>
	/// <typeparam name="TEntity"></typeparam>
	/// <param name="expr1"></param>
	/// <param name="expr2"></param>
	/// <remarks>inspired by Marc Gravell via: http://stackoverflow.com/questions/457316/combining-two-expressions-expressionfunct-bool</remarks>
	/// <returns></returns>
	public static Expression<Func<TEntity, bool>> OrElse<TEntity>(
		this Expression<Func<TEntity, bool>> expr1,
		Expression<Func<TEntity, bool>> expr2)
	{
		ParameterExpression param = expr1.Parameters[0];
		if (ReferenceEquals(param, expr2.Parameters[0]))
		{
			return Expression.Lambda<Func<TEntity, bool>>(Expression.OrElse(expr1.Body, expr2.Body), param);
		}
		return Expression.Lambda<Func<TEntity, bool>>(

			Expression.OrElse(expr1.Body, Expression.Invoke(expr2, param)), param);
	}
	/// <summary>
	/// From BReusable
	/// </summary>
	/// <param name="objectList"></param>
	/// <param name="t"></param>
	/// <returns></returns>
	public static bool ContainsType(this IList<object> objectList, Type t)
	{

		foreach (var item in objectList)
		{
			if (item.GetType() == t)
				return true;
		}
		return false;
	}



	/// <summary>
	/// From BReusable
	/// </summary>
	/// <typeparam name="T"></typeparam>
	/// <param name="list"></param>
	/// <returns></returns>
	public static T? SingleOrNull<T>(this IEnumerable<T> list) where T : struct
	{
		if (list.Count() == 1)
			return list.Single();
		return null;
	}

	/// <summary>
	/// From BReusable
	/// </summary>
	/// <param name="dc"></param>
	/// <param name="rowType"></param>
	/// <returns></returns>
	public static bool HasChanges(this DataContext dc, Type rowType)
	{
		var changes = dc.GetChangeSet();
		if (changes.Deletes.ContainsType(rowType) || changes.Inserts.ContainsType(rowType) || changes.Updates.ContainsType(rowType))
			return true;
		return false;
	}

	/// <summary>
	/// From BReusable
	/// </summary>
	/// <param name="dc"></param>
	/// <returns></returns>
	public static bool HasChanges(this DataContext dc)
	{
		var changes = dc.GetChangeSet();//TODO: find bug here
		if (changes.Inserts.Count > 0 || changes.Updates.Count > 0 || changes.Deletes.Count > 0)
			return true;
		return false;
	}

    ///// <summary>
    ///// From BReusable
    ///// </summary>
    ///// <typeparam name="TItem"></typeparam>
    ///// <typeparam name="TResult"></typeparam>
    ///// <param name="list"></param>
    ///// <param name="doWhatIfNotDefault"></param>
    ///// <returns></returns>
    //public static TResult FirstOrDefault<TItem, TResult>(this IEnumerable<TItem> list, Func<TItem, TResult> doWhatIfNotDefault) where TItem : class
    //{
    //    if (list == null || list.FirstOrDefault() == null)
    //        return default(TResult);
    //    return doWhatIfNotDefault(list.First());
    //}

	/// <summary>
	/// Used on columns that are one to many to display either null, the value, or the count if >1
	/// From BReusable
	/// </summary>
	/// <typeparam name="TItem"></typeparam>
	/// <param name="list"></param>
	/// <param name="getDescriptionIfSingle"></param>
	/// <returns></returns>
	public static String ToSummaryString<TItem>(this IEnumerable<TItem> list, Func<TItem, String> getDescriptionIfSingle)
	{
		var count = list.Count();
		return count > 1 ? count.ToString() :
			count == 1 ? getDescriptionIfSingle(list.Single()) :
				string.Empty;


	}

	/// <summary>
	/// From BReusable
	/// </summary>
	/// <typeparam name="TItem"></typeparam>
	/// <param name="list"></param>
	/// <param name="action"></param>
	public static void IfSingle<TItem>(this IEnumerable<TItem> list, Action<TItem> action)
	{
		if (list.Count() == 1)
			action(list.Single());
	}

	/// <summary>
	/// From BReusable
	/// </summary>
	/// <typeparam name="TItem"></typeparam>
	/// <param name="list"></param>
	/// <param name="action"></param>
	public static void FirstIfNotDefault<TItem>(this IEnumerable<TItem> list, Action<TItem> action)
	{
		if (list.Count() > 0)
			action(list.First());
	}

	/// <summary>
	/// From BReusable
	/// </summary>
	/// <typeparam name="TItem"></typeparam>
	/// <param name="list"></param>
	/// <param name="boolean"></param>
	/// <param name="onTrue"></param>
	/// <param name="onFalse"></param>
	public static void IIF<TItem>(this IEnumerable<TItem> list
		, Func<IEnumerable<TItem>, bool> boolean, Action<IEnumerable<TItem>> onTrue
		, Action<IEnumerable<TItem>> onFalse)
	{
		if (boolean(list)) onTrue(list); else onFalse(list);
	}
	/// <summary>
	/// From BReusable
	/// </summary>
	/// <param name="dc"></param>
	/// <remarks>http://www.davidhayden.com/blog/dave/archive/2007/08/17/DataContextLogLoggingLINQToSQLOutputConsoleDebuggerOuputWindow.aspx</remarks>
	public static void SendQueriesToConsole(this DataContext dc)
	{
		dc.Log = Console.Out;
	}

	/// <summary>
	/// From BReusable
	/// </summary>
	/// <param name="dc"></param>
	/// <remarks>http://www.u2u.info/Blogs/Kris/Lists/Posts/Post.aspx?ID=11</remarks>
	public static void SendQueriesToDebug(this DataContext dc)
	{
		dc.Log = new DebuggerWriter();

	}

	/// <summary>
	/// From BReusable
	/// </summary>
	/// <param name="columnAttribute"></param>
	/// <returns></returns>
	public static int? GetColumnLength(this ColumnAttribute columnAttribute)
	{
		string dbType = columnAttribute.DbType;
		if (dbType.IsNullOrEmpty() == false && (dbType.Contains("Char(")))
		{
			int index1 = dbType.IndexOf("(");
			int index2 = dbType.IndexOf(")");
			string dbLen = dbType.Substring(index1 + 1, index2 - index1 - 1);
			int result;
			if (int.TryParse(dbLen, out result))
				return result;
			return null;
		}
		return null;
	}

}
